#ifndef G3_ZSTREAM_H
#define G3_ZSTREAM_H

#include "f_stream.h"

// When using stdin or stdout for compressed streaming, then the following
// hack should be used..
//
// This is an ugly hack required to avoid corruption of the input and output
// data on Windows/MS-DOS systems. Without this, those systems would assume
// that the input and output files are text, and try to convert the end-of-line
// characters from one standard to another.
#if defined(MSDOS) || defined(OS2) || defined(WIN32) || defined(__CYGWIN__)
#  include <fcntl.h>
#  include <io.h>
#  define SET_BINARY_MODE(file) setmode(fileno(file), O_BINARY)
#else
#  define SET_BINARY_MODE(file)
#endif

#define ZSTREAM_OPEN		4
#define ZSTREAM_DEFLATE		8
#define ZSTREAM_INFLATE		16

// Default ChunkSize: 256 KB
#define ZSTREAM_DEF_CHUNK	262144

namespace G3 {

class ZSTREAM {
	public:
		ZSTREAM (G3::STREAM *stream);
		virtual ~ZSTREAM ();

		LOG *log;

		void Disable (ushort flag);
		void Enable (ushort flag);

//==============================================================================
// Opens a ZFILESTREAM either for Deflating or Inflating data..
//
//		byte OpenFlags				Flags for the stream
//				ZSTREAM_DEFLATE		Opens for deflating (compressed buffering)
//				ZSTREAM_INFLATE		Opens for inflating (decompressed reading)
//
//		int Level					Compression level
//				-1					Default, level 6
//				0					No compression at all
//				1 ... 9				Lowest ... Hightest compression
//
//	Returns true if successful
//==============================================================================
		virtual bool Open (byte openFlags = ZSTREAM_INFLATE, int level = -1) = 0;

//==============================================================================
// Returns true if the ZFILESTREAM is opened with the given flags
//
//		byte OpenFlags				Flags the stream should be opened with
//				ZSTREAM_DEFLATE		Checks if the stream is opened for deflating
//				ZSTREAM_INFLATE		Checks if the stream is opened for inflating
//
//	Returns true if the test was successful
//==============================================================================
		bool IsOpen (byte openFlags = ZSTREAM_INFLATE);

//==============================================================================
// Closes the ZFILESTREAM
//==============================================================================
		virtual void Close () = 0;

//==============================================================================
// Sets the size of a chunk (deflation buffer)
//
//		long ChunkSize			According to zlib documentation, this should be
//								either 131072 (128 KB) or 262144 (256 KB)
//==============================================================================
		void SetChunkSize (long chunkSize = ZSTREAM_DEF_CHUNK);

//==============================================================================
// Compresses data and adds it to the stream buffer
//
//		void *Pointer			Pointer to a memory block from where to compress
//
//		long Size				Number of bytes to compress
//
//	NOTE:: To end the compression, either Pointer has to become NULL or Size 0
//
//	Returns true on success
//==============================================================================
		virtual bool Deflate (byte *pointer = NULL, long size = 0) = 0;

//==============================================================================
// Reads data from the stream and decompresses it
//
//		void *Pointer			Pointer to a memory block where the data is stored
//
//		long Size				Number of bytes saved at the Pointer
//
//	Returns the number of bytes stored at Pointer
//==============================================================================
		virtual long Inflate (byte *pointer, long size) = 0;

//==============================================================================
// Reads data into the stream and decompresses it
//
//		void *Pointer			Pointer to a memory block where the data is read from
//
//		long Size				Number of bytes saved into the stream
//
//	Returns the number of bytes stored into the stream
//==============================================================================
		virtual long InflateIn (byte *pointer, long size) = 0;

//==============================================================================
// Compresses data from the stream
//
//		void *Pointer			Pointer to a memory block where to compress
//
//		long Size				Number of bytes to compress
//
//	NOTE:: To end the compression, Size has to become 0
//
//	Returns true on success
//==============================================================================
		virtual long DeflateOut (byte *pointer = NULL, long size = 0) = 0;

//==============================================================================
// Reads data from a file and decompresses it
//
//		const char *Filename	Name of the file to buffer
//
//		long Size				Number of bytes to buffer
//				-1, 0			Buffers the whole file into the stream
//
//		long Position			Where in the file should we start reading
//
//	Returns true on success
//==============================================================================
		virtual long InflateFromFile (const string &filename, long size = -1, long position = 0) = 0;

//==============================================================================
// Reads data from the stream and compresses it into a file
//
//		const char *Filename	Name of the file to store the data in
//
//		long Size				Number of bytes read from the stream
//				-1, 0			Reads the whole stream buffer
//
//		long Position			Where to write into the file
//
//	Returns the number of bytes read
//==============================================================================
		virtual long DeflateToFile (const string &filename, long size = -1, long position = 0) = 0;

	protected:
		char compressionLevel;
		long outputChunkSize;

		ushort flags;

		G3::STREAM *bStream;
};

}

#endif
